#include "PointLight.h"

using namespace OBR;

std::shared_ptr<CudaProgram> PointLight::program = nullptr;
CudaBuffer PointLight::sbt_data_payload;

PointLight::PointLight() : DeltaLight("shaders/Light/PointLight.cu")
{
    // init program opt & desc
    this->program_desc.callables.entryFunctionNameDC = "__direct_callable__point_light";
    this->program_desc.kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES;
    this->program_desc.flags = OPTIX_PROGRAM_GROUP_FLAGS_NONE;
}

PointLight::~PointLight()
{
}

void PointLight::_buildFromConfig(const Json::Value &config)
{
    this->position = ConfigHelpers::loadFloat3(config["position"]);
    ConfigHelpers::loadSpectrum(config["intensity"], &(this->intensity));
}

void PointLight::uploadVirtualProgramPayload(
    std::vector<std::shared_ptr<VirtualProgramEntry>> instances)
{
    const size_t n_instances = instances.size();
    const size_t data_size = sizeof(PointLightData);
    const size_t memory_size = n_instances * data_size;

    // collect data
    PointLightData *tmpdata = new PointLightData[n_instances];
    for (size_t i = 0; i < n_instances; i++)
    {
        auto l = std::dynamic_pointer_cast<PointLight>(instances[i]);
        tmpdata[i].position = l->position;
        tmpdata[i].intensity = l->intensity;
    }

    // upload data and free tmp
    PointLight::sbt_data_payload.resize(memory_size);
    PointLight::sbt_data_payload.upload(tmpdata, memory_size);
    delete[] tmpdata;
}

CUdeviceptr PointLight::getPayloadDevicePtr()
{
    return PointLight::sbt_data_payload.d_pointer();
}

void PointLight::setProgram(std::shared_ptr<CudaProgram> prog)
{
    PointLight::program = prog;
}

std::shared_ptr<CudaProgram> PointLight::getProgram()
{
    return PointLight::program;
}

OptixProgramGroupOptions &PointLight::getProgramGroupOptions()
{
    return this->program_opt;
}

OptixProgramGroupDesc &PointLight::getProgramGroupDesc()
{
    this->program_desc.callables.moduleDC = this->cuda_module->optix_module;
    return this->program_desc;
}

void PointLight::updateSbtData()
{
    assert(this->sbt_data_payload.d_ptr != nullptr);
    PointLightData *d_loc =
        (PointLightData *)this->sbt_data_payload.d_ptr + this->program_instance_id;
    PointLightData data = {this->position, this->intensity};
    CUDA_CHECK(cudaMemcpy(d_loc, &data, sizeof(PointLightData), cudaMemcpyHostToDevice));
}